#include "audio-video.h"
#include "log-print.h"
#include <pthread.h>
#include <queue>
#include <cstdint>
#include <android/log.h>

#define MAX_READ_CACHE 60
#define MAX_AUDIO_FRAME_SIZE 48000 * 4

extern "C" {
  #include<libavcodec/avcodec.h>
  #include<libavformat/avformat.h>
  #include<libavutil/imgutils.h>
  #include<libswscale/swscale.h>
  #include<libswresample/swresample.h>
  #include<ctime>
}

using namespace std;

struct FFmpegInfo{
    AVFormatContext *pFormatCtx;//封装格式上下文
    AVCodecContext *pVideoCodecCtx;//视频解码器上下文
    AVCodecContext *pAudioCodecCtx;//音频解码器上下文
    const AVCodec * pVideoCodec;//视频解码器
    const AVCodec * pAudioCodec;//音频解码器
    unsigned int max_stream;//总共有多少流
    int video_stream_index;//视频流下标
    int audio_stream_count;//音频流个数
    int *audio_stream_index;//音频流下标集合
    int subtitle_stream_count;//字幕流个数
    int *subtitle_stream_index;//字幕流下标集合
    int64_t video_clock;//当前视频的时间
    int64_t audio_clock;//当前音频的时间
};

struct POSIXInfo{
    pthread_t read_id;//读文件的线程id
    pthread_t playVideo_id;//播放视频的线程id
    pthread_t playAudio_id;//播放音频的线程id
    pthread_mutex_t pthread_mutex;
    pthread_cond_t pthread_cond;
};

FFmpegInfo *ffmpegInfo;
queue<AVPacket*> videoAVPackets;//未解码的视频数据缓存队列
queue<AVPacket*> audioAVPackets;//未解码的音频数据缓存队列
POSIXInfo* posixInfo;
DecodecListener* pDecodecListener;//解码监听接口

bool decodec_flag = false;

/**
 * 注册组件
 */
void initFFmpeg() {}

/**
 * 初始化结构体FFmpegInfo
 * 初始化视频解码器和音频解码器
 * @param path
 */
void initFFmpegInfo(const char *path) {
    ffmpegInfo = (FFmpegInfo *) malloc(sizeof(FFmpegInfo));
    //初始化 AVFormatContext *pFormatCtx
    ffmpegInfo->pFormatCtx = avformat_alloc_context();
    if (avformat_open_input(&ffmpegInfo->pFormatCtx, path, nullptr, nullptr) != 0) {
        ALOGD("打开输入文件失败");
        return;
    }

    if (avformat_find_stream_info(ffmpegInfo->pFormatCtx, nullptr) < 0) {
        ALOGD("获取视频文件信息失败");
        return;
    }

    //输出视频信息
    av_dump_format(ffmpegInfo->pFormatCtx, -1, path, 0);
    ffmpegInfo->max_stream = ffmpegInfo->pFormatCtx->nb_streams > 0 ? ffmpegInfo->pFormatCtx->nb_streams : 0;
    if (ffmpegInfo->max_stream == 0) {
        ALOGD("没有流");
        return;
    }
    ffmpegInfo->audio_stream_count = 0;
    ffmpegInfo->subtitle_stream_count = 0;
    for (int i = 0; i < ffmpegInfo->max_stream; ++i) {
        if (ffmpegInfo->pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
            //获取视频流的索引（下标）位置
            ffmpegInfo->video_stream_index = i;
        } else if (ffmpegInfo->pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
            //音频流个数+1
            ffmpegInfo->audio_stream_count++;
        } else if (ffmpegInfo->pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE) {
            //字幕流个数+1
            ffmpegInfo->subtitle_stream_count++;
        }
    }
    //获得音频流和字幕流下标集合
    AVMediaType temp[] = {AVMEDIA_TYPE_AUDIO, AVMEDIA_TYPE_SUBTITLE};
    for (auto type : temp) {
        int index = 0;
        if (type == AVMEDIA_TYPE_AUDIO && ffmpegInfo->audio_stream_count > 0) {
            //存放音频流下标集合
            ffmpegInfo->audio_stream_index = (int *) malloc(sizeof(int) * ffmpegInfo->audio_stream_count);
            for (int i = 0; i < ffmpegInfo->max_stream; ++i) {
                if (ffmpegInfo->pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
                    ffmpegInfo->audio_stream_index[index++] = i;
                }
            }
        } else if (type == AVMEDIA_TYPE_SUBTITLE && ffmpegInfo->subtitle_stream_count > 0) {
            //存放字幕流下标集合
            ffmpegInfo->subtitle_stream_index = (int *) malloc(sizeof(int) * ffmpegInfo->subtitle_stream_count);
            for (int i = 0; i < ffmpegInfo->max_stream; ++i) {
                if (ffmpegInfo->pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE) {
                    ffmpegInfo->subtitle_stream_index[index++] = i;
                }
            }
        }
    }

    if (ffmpegInfo->video_stream_index == -1) {
        ALOGD("没有视频流");
    } else {
        ffmpegInfo->pVideoCodecCtx = avcodec_alloc_context3(nullptr);
        if (ffmpegInfo->pVideoCodecCtx == nullptr) {
             return;
        }
        avcodec_parameters_to_context(ffmpegInfo->pVideoCodecCtx, ffmpegInfo->pFormatCtx->streams[ffmpegInfo->video_stream_index]->codecpar);
        ffmpegInfo->pVideoCodec = avcodec_find_decoder(ffmpegInfo->pVideoCodecCtx->codec_id);
        //打开编码器
        if (avcodec_open2(ffmpegInfo->pVideoCodecCtx, ffmpegInfo->pVideoCodec, nullptr) < 0) {
            ALOGD("打开视频编码器失败");
            return;
        }
    }

    if (ffmpegInfo->audio_stream_count == 0) {
        ALOGD("没有音频流");
    } else {
        //获取第一个音频流的解码器上下文
        ffmpegInfo->pAudioCodecCtx = avcodec_alloc_context3(nullptr);
        if (ffmpegInfo->pAudioCodecCtx == nullptr) {
            return;
        }
        avcodec_parameters_to_context(ffmpegInfo->pAudioCodecCtx, ffmpegInfo->pFormatCtx->streams[ffmpegInfo->audio_stream_index[0]]->codecpar);
        ffmpegInfo->pAudioCodec = avcodec_find_decoder(ffmpegInfo->pAudioCodecCtx->codec_id);
        if (avcodec_open2(ffmpegInfo->pAudioCodecCtx, ffmpegInfo->pAudioCodec, nullptr) < 0) {
            ALOGD("打开音频编码器失败");
            return;
        }
    }

    if (ffmpegInfo->subtitle_stream_count == 0) {
        ALOGD("没有字幕流");
    }
}

void destroyFFmpegInfo() {
    //关闭解编码器
    if (ffmpegInfo->pVideoCodecCtx != nullptr) {
        avcodec_close(ffmpegInfo->pVideoCodecCtx);
    }
    if (ffmpegInfo->pAudioCodecCtx != nullptr) {
        avcodec_close(ffmpegInfo->pAudioCodecCtx);
    }
    if (ffmpegInfo->pFormatCtx != nullptr) {
        //关闭输入文件
        avformat_close_input(&ffmpegInfo->pFormatCtx);
    }
    if (ffmpegInfo->audio_stream_index != nullptr) {
        free(ffmpegInfo->audio_stream_index);
        ffmpegInfo->audio_stream_index = nullptr;
    }
    if (ffmpegInfo->subtitle_stream_index != nullptr) {
        free(ffmpegInfo->subtitle_stream_index);
        ffmpegInfo->subtitle_stream_index = nullptr;
    }
    free(ffmpegInfo);
    ffmpegInfo = nullptr;
}

void initPOSIXInfo() {
    posixInfo = (POSIXInfo *) malloc(sizeof(POSIXInfo));
    pthread_mutex_init(&posixInfo->pthread_mutex, nullptr);
    pthread_cond_init(&posixInfo->pthread_cond, nullptr);
}

void destroyPOSIXInfo() {
    pthread_mutex_destroy(&posixInfo->pthread_mutex);
    pthread_cond_destroy(&posixInfo->pthread_cond);
    free(posixInfo);
    posixInfo = nullptr;
}

/**
 * 读数据
 * @param arg
 * @return
 */
void *run_read(void *arg) {
    ALOGD("read 线程 running");
    AVPacket *pPacket = av_packet_alloc();
    //av_seek_frame(ffmpegInfo->pFormatCtx,ffmpegInfo->video_stream_index,5000,NULL);
    //av_seek_frame(ffmpegInfo->pFormatCtx,ffmpegInfo->audio_stream_indexs[0],5000,NULL);
    while (av_read_frame(ffmpegInfo->pFormatCtx, pPacket) == 0) {
        ALOGD("读ing");
        //读取
        pthread_mutex_lock(&posixInfo->pthread_mutex);
        AVPacket *temp;
        if (pPacket->stream_index == ffmpegInfo->video_stream_index) {
            temp = av_packet_alloc();
            av_packet_ref(temp, pPacket);
            videoAVPackets.push(temp);
            ALOGD("视频缓冲区大小：%lu", videoAVPackets.size());
        } else if (pPacket->stream_index == ffmpegInfo->audio_stream_index[0]) {
            temp = av_packet_alloc();
            av_packet_ref(temp, pPacket);
            audioAVPackets.push(temp);
            ALOGD("音频缓冲区大小：%lu", audioAVPackets.size());
        }

        while (videoAVPackets.size() >= MAX_READ_CACHE || audioAVPackets.size() >= MAX_READ_CACHE) {
            pthread_cond_wait(&posixInfo->pthread_cond, &posixInfo->pthread_mutex);
        }

        pthread_cond_broadcast(&posixInfo->pthread_cond);
        pthread_mutex_unlock(&posixInfo->pthread_mutex);
    }

    decodec_flag = false;
    av_free(pPacket);
    ALOGD("end--end--视频缓冲区大小：%lu", videoAVPackets.size());
    ALOGD("end--end--音频缓冲区大小：%lu", audioAVPackets.size());
    return nullptr;
}

/**
 * 启动读数据的线程
 */
void read() {
    pthread_create(&posixInfo->read_id, nullptr, run_read, nullptr);
}

/**
 * 获取视频数据并解码
 * @return
 */
void *run_video_decodec(void *) {
    //int countFrame = 0;
    //int64_t start_PTS = 0;
    //初始化 AVFrame *pFrame -> 存放解码后的数据
    AVFrame *pFrame = av_frame_alloc();
    //AVFrame *pFrameRGBA -> 存放转换为RGBA后的数据
    AVFrame *pFrameRGBA = av_frame_alloc();
    int w = ffmpegInfo->pVideoCodecCtx->width;
    int h = ffmpegInfo->pVideoCodecCtx->height;
    auto oneFrameSize = (size_t) av_image_get_buffer_size(AV_PIX_FMT_RGBA, w, h, 1);
    auto *buff = (uint8_t *) malloc(oneFrameSize);

    //初始化用于格式转换的SwsContext，由于解码出来的帧格式不是RGBA的,在渲染之前需要进行格式转换
    SwsContext *sws_ctx = sws_getContext(w, h, ffmpegInfo->pVideoCodecCtx->pix_fmt,
                                         w, h, AV_PIX_FMT_RGBA,
                                         SWS_BILINEAR,
                                         nullptr,
                                         nullptr,
                                         nullptr);
    if (pDecodecListener != nullptr && pDecodecListener->startDecodecVideo != nullptr) {
        pDecodecListener->startDecodecVideo(nullptr);
    }

    //开始播放的时间
    //int64_t startTime = av_gettime();

    double time_base_d = av_q2d(ffmpegInfo->pFormatCtx->streams[ffmpegInfo->video_stream_index]->time_base);
    //int frameInterval = (int)(1/(time_base_d * av_q2d(ffmpegInfo->pFormatCtx->streams[ffmpegInfo->video_stream_index]->avg_frame_rate)));
    //帧率
    //int frameRate = (int)(av_q2d(ffmpegInfo->pFormatCtx->streams[ffmpegInfo->video_stream_index]->avg_frame_rate));
    ALOGD("视频解码ing,%lu", videoAVPackets.size());
    while (decodec_flag) {
        pthread_mutex_lock(&posixInfo->pthread_mutex);

        while (videoAVPackets.empty()) {
            pthread_cond_wait(&posixInfo->pthread_cond, &posixInfo->pthread_mutex);
        }

        AVPacket *pPacket = videoAVPackets.front();
        videoAVPackets.pop();

        pthread_cond_broadcast(&posixInfo->pthread_cond);
        pthread_mutex_unlock(&posixInfo->pthread_mutex);

        int packet_value = avcodec_send_packet(ffmpegInfo->pVideoCodecCtx, pPacket);
        if (packet_value != 0) {
            return nullptr;
        }

        while (avcodec_receive_frame(ffmpegInfo->pVideoCodecCtx, pFrame) >= 0) {
            if (pPacket->pts != AV_NOPTS_VALUE) {
                pFrame->pts = pPacket->pts;
            }
            /*else {
                pFrame->pts = av_frame_get_best_effort_timestamp(pFrame);
            }*/

            // 计算当前帧的PTS
            //int64_t thisFramePTS = start_PTS + frameInterval * countFrame;
            auto thisFramePTS = (double) pFrame->pts;
            //PTS * time_base = 当前显示的时间（单位s)
            auto time = (int64_t) (thisFramePTS * time_base_d);
            //PTS * time_base = 当前显示的时间（单位s),*1000000转换为微秒
            ffmpegInfo->video_clock = (int64_t) ((thisFramePTS * time_base_d) * 1000000);
            //countFrame++;
            av_image_fill_arrays(pFrameRGBA->data,
                                 pFrameRGBA->linesize,
                                 (uint8_t *) buff,
                                 AV_PIX_FMT_RGBA,
                                 w, h, 1);
            //格式转换
            sws_scale(sws_ctx,
                      (const uint8_t *const *) pFrame->data,
                      pFrame->linesize,
                      0, h,
                      pFrameRGBA->data,
                      pFrameRGBA->linesize);
            if (pDecodecListener != nullptr && pDecodecListener->onDecodecVideo != nullptr) {
                //调用回调接口进行绘制
                pDecodecListener->onDecodecVideo(buff, oneFrameSize, w, h, time);
            }

            //视频同步到音频
            //视频比音频快
            //if (ffmpegInfo->video_clock > ffmpegInfo->audio_clock) {
                //延迟
                //av_usleep((uint) (ffmpegInfo->video_clock - ffmpegInfo->audio_clock));
            //} else {
                //音频比视频快
                //不做延迟
            //}

           //计算标准视频时间（音频同步到视频需要）
           //auto showTime = (int64_t)(startTime + ffmpegInfo->video_clock);
           //当前时间
           //int64_t thisTime = av_gettime();
           //if (thisTime < showTime) {
                //显示快了，需要放慢速度
                //av_usleep((uint) (showTime - thisTime));
           //} else if (thisTime > showTime) {
                //显示慢了
           //} else {
                //刚好显示时间正确，一般不会走到这里
           //}
        }

        av_packet_unref(pPacket);
        av_free(pPacket);
    }

    free(buff);
    av_free(pFrame);
    av_free(pFrameRGBA);
    sws_freeContext(sws_ctx);

    if (pDecodecListener != nullptr && pDecodecListener->endDecodecVideo != nullptr) {
        pDecodecListener->endDecodecVideo(nullptr);
    }

    ALOGD("视频解码结束");
    return nullptr;
}

/**
 * 获取音频数据并解码
 * @param arg
 * @return
 */
void* run_audio_decodec(void* arg) {
    //初始化 AVFrame *pFrame -> 存放解码后的数据
    AVFrame *pFrame = av_frame_alloc();
    //初始化用于重采样的SwrContext
    //分配重采样SwrContext
    SwrContext *swrCtx = swr_alloc();
    //输入的采样格式
    enum AVSampleFormat in_sample_fmt = ffmpegInfo->pAudioCodecCtx->sample_fmt;
    //输出采样格式16bit PCM
    enum AVSampleFormat out_sample_fmt = AV_SAMPLE_FMT_S16;
    //输入采样率
    int in_sample_rate = ffmpegInfo->pAudioCodecCtx->sample_rate;
    //输出采样率
    int out_sample_rate = 44100;
    //获取输入的声道布局
    //auto in_ch_layout = (int64_t)(ffmpegInfo->pAudioCodecCtx->channel_layout);
    //输出的声道布局（立体声）
    //int64_t out_ch_layout = AV_CH_LAYOUT_STEREO;
    /*
     * struct SwrContext *swr_alloc_set_opts(struct SwrContext *s,
                                      int64_t out_ch_layout, enum AVSampleFormat out_sample_fmt, int out_sample_rate,
                                      int64_t  in_ch_layout, enum AVSampleFormat  in_sample_fmt, int  in_sample_rate,
                                      int log_offset, void *log_ctx);
     * */

    /*int swr_alloc_set_opts2(struct SwrContext **ps,
                            const AVChannelLayout *out_ch_layout, enum AVSampleFormat out_sample_fmt, int out_sample_rate,
                            const AVChannelLayout *in_ch_layout, enum AVSampleFormat  in_sample_fmt, int  in_sample_rate,
                            int log_offset, void *log_ctx);*/
    //设置参数到SwrContext
    swr_alloc_set_opts2(&swrCtx,
                        &ffmpegInfo->pAudioCodecCtx->ch_layout,
                        out_sample_fmt,
                        out_sample_rate,
                        &ffmpegInfo->pAudioCodecCtx->ch_layout,
                        in_sample_fmt,
                        in_sample_rate,
                        0,
                        nullptr
                        );
    /*swr_alloc_set_opts(swrCtx,
                       out_ch_layout,
                       out_sample_fmt,
                       out_sample_rate,
                       in_ch_layout,
                       in_sample_fmt,
                       in_sample_rate,
                       0,
                       nullptr);*/
    //初始化SwrContext
    swr_init(swrCtx);
    //输出的声道个数
    //int out_channel_nb = av_get_channel_layout_nb_channels(out_ch_layout);
    int out_channel_nb = ffmpegInfo->pAudioCodecCtx->ch_layout.nb_channels;
    //分配存放16bit 44100PCM数据的内存
    auto *out_buffer = (uint8_t *) av_malloc(MAX_AUDIO_FRAME_SIZE);

    if (pDecodecListener != nullptr && pDecodecListener->startDecodecAudio != nullptr) {
        pDecodecListener->startDecodecAudio(out_sample_rate, out_channel_nb);
    }

    //开始播放的时间
    //int64_t startTime = av_gettime();
    double time_base_d = av_q2d(ffmpegInfo->pFormatCtx->streams[ffmpegInfo->audio_stream_index[0]]->time_base);

    ALOGD("音频解码ing,%lu", audioAVPackets.size());
    while (decodec_flag) {
        pthread_mutex_lock(&posixInfo->pthread_mutex);

        while (audioAVPackets.empty()) {
            pthread_cond_wait(&posixInfo->pthread_cond, &posixInfo->pthread_mutex);
        }

        AVPacket *pPacket = audioAVPackets.front();
        audioAVPackets.pop();

        pthread_cond_broadcast(&posixInfo->pthread_cond);
        pthread_mutex_unlock(&posixInfo->pthread_mutex);

        int packet_value = avcodec_send_packet(ffmpegInfo->pAudioCodecCtx, pPacket);
        if (packet_value != 0) {
            return nullptr;
        }

        while (avcodec_receive_frame(ffmpegInfo->pAudioCodecCtx, pFrame) == 0) {
            if (pPacket->pts != AV_NOPTS_VALUE) {
                pFrame->pts = pPacket->pts;
            } else {
                AVRational tb = (AVRational) {1, pFrame->sample_rate};
                if (pFrame->pts != AV_NOPTS_VALUE) {
                    pFrame->pts = av_rescale_q(pFrame->pts,
                                               ffmpegInfo->pAudioCodecCtx->time_base,
                                               tb);
                } else if (pFrame->pts != AV_NOPTS_VALUE) {
                    pFrame->pts = av_rescale_q(pFrame->pts, tb, tb);
                } else {
                    pFrame->pts = pPacket->dts;
                }
            }

            auto thisFramePTS = (double) pFrame->pts;
            //PTS * time_base = 当前显示的时间（单位s）* 1000000 转化为 微秒
            //ffmpegInfo->audio_clock = (int64_t) (thisFramePTS * 1000000 * time_base_d);
            ffmpegInfo->audio_clock = (int64_t) (thisFramePTS * time_base_d * 1000000);

            //AudioTrack是有阻塞的所以可以不需要延迟，因为它按正常速度执行就是标准的音频时间
            //bool sleepFlag = false;
            //if (sleepFlag) {
                //计算音频标准时间（视频同步到音频需要）
                //int64_t showTime = (int64_t) (startTime + ffmpegInfo->audio_clock);
                //int64_t thisTime = av_gettime();//当前时间
                //if (thisTime < showTime) { //显示快了，需要放慢速度
                //    av_usleep((uint) (showTime - thisTime));
                //} else if (thisTime > showTime) {
                //    //显示慢了，需要加快速度
                //} else {
                //    //刚好显示时间正确，一般不会走到这里
                //}
            //}

            //重采样
            swr_convert(swrCtx,
                        &out_buffer,
                        MAX_AUDIO_FRAME_SIZE,
                        (const uint8_t **) pFrame->data,
                        pFrame->nb_samples);
            //获取samples（样本，类似于视频的一帧）的大小
            int out_buffer_size = av_samples_get_buffer_size(nullptr,
                                                             out_channel_nb,
                                                             pFrame->nb_samples,
                                                             out_sample_fmt,
                                                             1);
            //调用回调接口进行播放音频
            if (pDecodecListener != nullptr && pDecodecListener->onDecodecAudio != nullptr) {
                pDecodecListener->onDecodecAudio(out_buffer, out_buffer_size);
            }
        }

        av_packet_unref(pPacket);
        av_free(pPacket);
    }

    av_free(pFrame);
    av_free(out_buffer);
    swr_free(&swrCtx);

    if (pDecodecListener != nullptr && pDecodecListener->endDecodecAudio != nullptr) {
        pDecodecListener->endDecodecAudio(nullptr);
    }

    ALOGD("音频解码结束");
    return nullptr;
}

void startDecodec() {
    ALOGD("startDecodec 开始解码");

    decodec_flag = true;
    pthread_create(&posixInfo->playVideo_id, nullptr, run_video_decodec, nullptr);
    pthread_create(&posixInfo->playAudio_id, nullptr, run_audio_decodec, nullptr);

    pthread_join(posixInfo->read_id, nullptr);
    pthread_join(posixInfo->playVideo_id, nullptr);
    pthread_join(posixInfo->playAudio_id, nullptr);
}

void stopDecodec() {
    decodec_flag = false;
}

void setDecodecListener(DecodecListener *decodecListener) {
    pDecodecListener = decodecListener;
}